revealer: Support minimum size of child
authorCarlos Soriano <csoriano@redhat.com>
Tue, 18 Sep 2018 08:55:02 +0000 (10:55 +0200)
committerCarlos Soriano <csoriano@redhat.com>
Tue, 18 Sep 2018 09:36:29 +0000 (11:36 +0200)
Up until now when allocating the child it only used the natural size
while the measuring also used the minimum size, resulting in a clipped
child when animating if the child had different minimum size and
natural size. This was an obvious case when using labels that had
ellipsization.

This commit gives full allocation to the child by inverting the size
the revealer reduces from its animation progress.

Code done by Benjamin Otte.

Closes: https://gitlab.gnome.org/GNOME/gtk/issues/635
gtk/gtkrevealer.c

index 13c37b9a8006bdce748ed61ec4cedd359f4a165a..b0dfc0c691f9b78c5efef94344fe440590f71f89 100644 (file)
@@ -294,72 +294,46 @@ effective_transition (GtkRevealer *revealer)
 }
 
 static void
-gtk_revealer_get_child_allocation (GtkRevealer         *revealer,
-                                   const GtkAllocation *allocation,
-                                   GtkAllocation       *child_allocation)
+gtk_revealer_real_add (GtkContainer *container,
+                       GtkWidget    *child)
 {
+  GtkRevealer *revealer = GTK_REVEALER (container);
   GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
-  GtkWidget *child;
-  GtkRevealerTransitionType transition;
-
-  g_return_if_fail (revealer != NULL);
-  g_return_if_fail (allocation != NULL);
-
-  child_allocation->x = 0;
-  child_allocation->y = 0;
-  child_allocation->width = 0;
-  child_allocation->height = 0;
-
-  child = gtk_bin_get_child (GTK_BIN (revealer));
-  if (child != NULL && gtk_widget_get_visible (child))
-    {
-      transition = effective_transition (revealer);
 
-      if (transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT ||
-          transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
-        gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL,
-                            MAX (0, allocation->height),
-                            NULL, &child_allocation->width, NULL, NULL);
-      else
-        gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL,
-                            MAX (0, allocation->width),
-                            NULL, &child_allocation->height, NULL, NULL);
-
-      child_allocation->width = MAX (child_allocation->width, allocation->width);
-      child_allocation->height = MAX (child_allocation->height, allocation->height);
+  g_return_if_fail (child != NULL);
 
-      switch (transition)
-        {
-        case GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT:
-            child_allocation->x = - child_allocation->width * (1 - priv->current_pos);
-          break;
-        case GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN:
-            child_allocation->y = - child_allocation->height * (1 - priv->current_pos);
-          break;
-
-        case GTK_REVEALER_TRANSITION_TYPE_NONE:
-        case GTK_REVEALER_TRANSITION_TYPE_CROSSFADE:
-        case GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT:
-        case GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP:
-        default:
-          break;
-        }
-    }
+  gtk_widget_set_child_visible (child, priv->current_pos != 0.0);
 
+  GTK_CONTAINER_CLASS (gtk_revealer_parent_class)->add (container, child);
 }
 
-static void
-gtk_revealer_real_add (GtkContainer *container,
-                       GtkWidget    *child)
+static double
+get_child_size_scale (GtkRevealer    *revealer,
+                      GtkOrientation  orientation)
 {
-  GtkRevealer *revealer = GTK_REVEALER (container);
   GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
 
-  g_return_if_fail (child != NULL);
+  switch (effective_transition (revealer))
+    {
+    case GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT:
+    case GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT:
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+        return priv->current_pos;
+      else
+        return 1.0;
 
-  gtk_widget_set_child_visible (child, priv->current_pos != 0.0);
+    case GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN:
+    case GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP:
+      if (orientation == GTK_ORIENTATION_VERTICAL)
+        return priv->current_pos;
+      else
+        return 1.0;
 
-  GTK_CONTAINER_CLASS (gtk_revealer_parent_class)->add (container, child);
+    case GTK_REVEALER_TRANSITION_TYPE_NONE:
+    case GTK_REVEALER_TRANSITION_TYPE_CROSSFADE:
+    default:
+      return 1.0;
+    }
 }
 
 static void
@@ -374,8 +348,32 @@ gtk_revealer_real_size_allocate (GtkWidget           *widget,
   if (child != NULL && gtk_widget_get_visible (child))
     {
       GtkAllocation child_allocation;
+      double hscale, vscale;
 
-      gtk_revealer_get_child_allocation (revealer, allocation, &child_allocation);
+      child_allocation = *allocation;
+
+      hscale = get_child_size_scale (revealer, GTK_ORIENTATION_HORIZONTAL);
+      vscale = get_child_size_scale (revealer, GTK_ORIENTATION_VERTICAL);
+
+      if (hscale <= 0 || vscale <= 0)
+        {
+          /* don't allocate anything, the child is invisible and the numbers
+           * don't make sense. */
+          return;
+        }
+      else if (hscale < 1.0)
+        {
+          g_assert (vscale == 1.0);
+          child_allocation.width = MIN (G_MAXINT, ceil (child_allocation.width / hscale));
+          if (effective_transition (revealer) == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
+            child_allocation.x = allocation->width - child_allocation.width;
+        }
+      else if (vscale < 1.0)
+        {
+          child_allocation.height = MIN (G_MAXINT, ceil (child_allocation.height / vscale));
+          if (effective_transition (revealer) == GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN)
+            child_allocation.y = allocation->height - child_allocation.height;
+        }
       gtk_widget_size_allocate (child, &child_allocation, -1);
     }
 }
@@ -552,46 +550,6 @@ gtk_revealer_get_child_revealed (GtkRevealer *revealer)
     return !reveal_child;
 }
 
-/* These all report only the natural size, ignoring the minimal size,
- * because its not really possible to allocate the right size during
- * animation if the child size can change (without the child
- * re-arranging itself during the animation).
- */
-
-static void
-set_height (GtkRevealer *revealer,
-            gint        *minimum_height,
-            gint        *natural_height)
-{
-  GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
-  GtkRevealerTransitionType transition;
-
-  transition = effective_transition (revealer);
-  if (transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP ||
-      transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN)
-    {
-      *minimum_height = round (*minimum_height * priv->current_pos);
-      *natural_height = round (*natural_height * priv->current_pos);
-    }
-}
-
-static void
-set_width (GtkRevealer *revealer,
-           gint        *minimum_width,
-           gint        *natural_width)
-{
-  GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
-  GtkRevealerTransitionType transition;
-
-  transition = effective_transition (revealer);
-  if (transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT ||
-      transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
-    {
-      *minimum_width = round (*minimum_width * priv->current_pos);
-      *natural_width = round (*natural_width * priv->current_pos);
-    }
-}
-
 static void
 gtk_revealer_measure (GtkWidget      *widget,
                       GtkOrientation  orientation,
@@ -601,15 +559,28 @@ gtk_revealer_measure (GtkWidget      *widget,
                       int            *minimum_baseline,
                       int            *natural_baseline)
 {
+  GtkRevealer *self = GTK_REVEALER (widget);
+  double scale;
+
+  scale = get_child_size_scale (self, OPPOSITE_ORIENTATION (orientation));
+
+  if (for_size >= 0)
+    {
+      if (scale == 0)
+        return;
+      else
+        for_size = MIN (G_MAXINT, ceil (for_size / scale));
+    }
+
   GTK_WIDGET_CLASS (gtk_revealer_parent_class)->measure (widget,
                                                          orientation,
                                                          for_size,
                                                          minimum, natural,
                                                          NULL, NULL);
-  if (orientation == GTK_ORIENTATION_HORIZONTAL)
-    set_width (GTK_REVEALER (widget), minimum, natural);
-  else
-    set_height (GTK_REVEALER (widget), minimum, natural);
+
+  scale = get_child_size_scale (self, orientation);
+  *minimum = ceil (*minimum * scale);
+  *natural = ceil (*natural * scale);
 }
 
 static void